
module Schnell
  class Element
    include Schnell::Exception
    def initialize(page,how,what,value=nil)
      @how = how
      @what = what
      @page = page
      @value = value
    end

    # used internally to locate the elements
    def locate
      if @how == :index
        elements = @page.findElementsByXPath("//#{self.class::ELEMENT_TYPE}")
        if (defined? self.class::INPUT_TYPES)
          elements = elements.select { |field| (self.class::INPUT_TYPES).include?(field.getAttribute("type"))}
        end
        @element = elements[@what - 1]
      elsif @how == :name
        @element = @page.findElementByXPath("//#{self.class::ELEMENT_TYPE}[@name='#{@what}']") if @value.nil?
        @element = @page.findElementByXPath("//#{self.class::ELEMENT_TYPE}[@name='#{@what}' and @value='#{@value}']") if ! @value.nil?
      elsif @how == :id
        @element = @page.findElementById(@what)
      elsif @how == :value
        elements = @page.findElementsByXPath("//#{self.class::ELEMENT_TYPE}[@value='#{@what}']")
        if (defined? self.class::INPUT_TYPES)
          selected_elements = elements.select { |field| (self.class::INPUT_TYPES).include?(field.getAttribute("type"))}
        end
        @element = selected_elements[0]
      elsif @how == :title
        @element = @page.findElementByXPath("//#{self.class::ELEMENT_TYPE}[@title='#{@what}']")
      else
        raise MissingWayOfFindingObjectException, "#{@how} is an unknown way of finding an <#{self.class.to_s}> element (#{@what})"
      end
    rescue
      @element = nil
    end
    
    private :locate

    #checks if the element is present, if it fails raises UnknownObjectException
    def assert_exists
      locate
      raise UnknownObjectException if @element.nil?      
    end
        
    #returns the element
    def object
      assert_exists
      return @element
    end

    #clicks the element located  
    #raises: UnknownObjectException  if the object is not found
    #ObjectDisabledException if the object is currently disabled    
    def click
      assert_exists
      assert_enabled if respond_to? :assert_enabled
      page = @element.click
      EnclosingElement.store = page
    end

    
    #returns true if the element exists, else if element not found it will return false
    def exists?
      begin
        assert_exists
      rescue UnknownObjectException
        return false
      end
      return true
    end
    
    # returns the textual representation of the element
    def text
      assert_exists
      return @element.getText
    end
    
    # returns the html representation of the element
    def html
      assert_exists
      return @element.to_s
    end
    
    #returns true if element is disabled, else it will return true
    def disabled
      assert_exists
      begin
        assert_enabled
      rescue
        return true
      end
      return false
    end

    #raises ObjectDisabledException or ObjectReadOnlyException if the text field is disabled or read only
    def assert_enabled
      assert_exists
      raise ObjectDisabledException if ! self.enabled?
      # Todo - Check if webdriver supports readonly property
      #~ raise ObjectReadOnlyException if @element.hasAttribute("readonly")
    end    
    
    #returns true if element is enabled, else it will return false
    def enabled?
      assert_exists
      @element.isEnabled()
    end    
        
    private
    def self.def_wrap(method_name,attribute_name)
      class_eval "def #{method_name}
                    assert_exists
                    begin
                      attribute = @element.getAttribute('#{attribute_name}')
                    rescue
                      attribute = nil
                    end
                    return attribute||''
                  end"
    end
    
    def self.def_wrap_guard(method_name)
      class_eval "def #{method_name}
                    assert_exists
                      begin
                        attribute = @element.getAttribute('#{method_name}')
                      rescue
                        attribute = nil
                      end
                      return attribute||''
                  end"
    end

    # return the name of the element (as defined in html)
    def_wrap_guard :name
    # return the id of the element
    def_wrap_guard :id
    # return the value of the element
    def_wrap_guard :value
    # return the title of the element
    def_wrap_guard :title
    # return the style of the element
    def_wrap_guard :style
    # return the class name of the element
    def_wrap :class_name, :class    
  end
end
